<template>
  <div class="app-container h-100% flex">
    <!-- 侧边栏/筛选项 -->
    <div class="left" :class="{ 'fold-inner-left': !sideOpened }">
      <sidebar-option
        @handle-render-car-line="onInitVehicleRoute"
        @handle-render-parking-marker="onRenderParkingMarker"
        @handle-render-parking-info="onRenderParkingInfo"
      ></sidebar-option>
    </div>
    <!-- 地图展示区域 -->
    <div class="flex-1 relative overflow-hidden">
      <div id="map-container" class="w-100% h-100%"></div>
      <!-- 展开收起按钮 -->
      <div class="hamburger-container" @click="sideOpened = !sideOpened">
        <el-icon color="#777C90" size="14px" class="fold-inner" :class="{ 'fold-inner-rotate': !sideOpened }">
          <ep-ArrowLeftBold />
        </el-icon>
      </div>
      <!-- 地址检索 -->
      <div class="absolute right-60px top-14px">
        <el-autocomplete
          v-model="keyword"
          :fetch-suggestions="handleSearch"
          :trigger-on-focus="false"
          clearable
          placeholder="地址检索"
          @select="handleSelect"
        >
          <template #prefix>
            <el-icon>
              <ep-Search />
            </el-icon>
          </template>
        </el-autocomplete>
      </div>
      <!-- 轨迹播放控制器 -->
      <div class="trajectory-player">
        <el-slider
          v-model="playerVal"
          :show-tooltip="false"
          :min="0"
          :max="pointNumber"
          size="small"
          :class="[showSpeedChart ? 'showBaseLine' : '']"
          @input="onSliderDrag"
          @change="onSliderChange"
        />
        <div class="player-controler">
          <div class="player-controler-left">
            <el-icon v-show="!isPlaying" :size="18" @click="playVehicleRoute"><ad-PlayCircleOutlined /></el-icon>

            <el-icon v-show="isPlaying" :size="18" @click="pauseVehicleRoute"><ad-PauseCircleOutlined /></el-icon>
          </div>
          <div class="player-controler-right">
            <span>{{ speedPerHour }} km/h</span>
            <el-dropdown class="ml-10px" @command="handleSpeedCommand">
              <span class="el-dropdown-link">倍速({{ playerMultiple + 'X' }})</span>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item :command="5">5.0X</el-dropdown-item>
                  <el-dropdown-item :command="3">3.0X</el-dropdown-item>
                  <el-dropdown-item :command="2">2.0X</el-dropdown-item>
                  <el-dropdown-item :command="1">1.0X</el-dropdown-item>
                  <el-dropdown-item :command="0.5">0.5X</el-dropdown-item>
                  <el-dropdown-item :command="0.25">0.25X</el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
            <span class="ml-10px" @click="showSpeedController = !showSpeedController">设置</span>
          </div>
        </div>

        <!-- 速度范围配置 -->
        <div v-show="showSpeedController" class="speed-controler">
          <div class="title">
            <span>设置</span>
            <i class="fa fa-close" style="color: #fff" @click="showSpeedController = false" />
          </div>
          <div class="content">
            <div class="content-left">轨迹颜色</div>
            <div class="content-right">
              <div class="progress-box">
                <div class="progress-left-blue" :style="{ width: `${speedRangeWidth[0]}%` }"></div>
                <el-slider
                  v-model="speedRange"
                  :max="120"
                  range
                  size="small"
                  @change="onSpeedRangeChange($event, true)"
                  @input="onSpeedRangeChange($event, false)"
                />
                <div class="progress-right-red" :style="{ width: `${speedRangeWidth[1]}%` }"></div>
              </div>
              <div class="scale-mark">
                <div v-for="(v, k) in 125 / 5" :key="v" class="mark-line">
                  <span :class="['line', k % 4 === 0 ? 'long-line' : '']">
                    <span v-if="k % 4 === 0" class="mark-num">{{ (v - 1) * 5 }}</span>
                  </span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- 速度曲线面板 -->
      <div :class="['speed-chart-container', showSpeedChart ? 'slide-up' : 'slide-down']">
        <div class="speed-chart-btn flex-c" @click="showSpeedChart = !showSpeedChart">
          <span>{{ `${showSpeedChart ? '收起' : '展示'}速度曲线` }}</span>
          <el-icon :size="12" :class="['ml-5', showSpeedChart ? 'rotate-90' : 'rotate--90']"><ep-DArrowRight /></el-icon>
        </div>
        <v-chart autoresize :option="echartOption" theme="light" />
      </div>
    </div>

    <div class="hidden">
      <!-- marker点带关闭按钮 -->
      <Marker ref="markerRef" @close="clearMarker" />
      <!-- 停车点弹框 -->
      <ParkInfo ref="parkInfoRef" :park-info="parkInfo" @close="handleCloseInfoWindow" />
      <!-- 车辆图标 -->
      <svg>
        <symbol
          id="vehiclePosition"
          version="1.1"
          xmlns="http://www.w3.org/2000/svg"
          xmlns:xlink="http://www.w3.org/1999/xlink"
          viewBox="0 0 1024 1024"
        >
          <path
            d="M0 512c0 282.766222 229.205333 512 512 512s512-229.233778 512-512S794.737778 0 512 0 0 229.233778 0 512z"
            p-id="125130"
          ></path>
          <path
            d="M533.660444 269.425778l195.072 390.172444a24.177778 24.177778 0 0 1-32.440888 32.512l-173.511112-86.016a24.305778 24.305778 0 0 0-21.546666 0l-173.511111 86.016a24.177778 24.177778 0 0 1-32.440889-32.512L490.24 269.425778a24.291556 24.291556 0 0 1 43.420444 0z"
            fill="#FFFFFF"
            p-id="125131"
          ></path>
        </symbol>
      </svg>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { useMap } from '@/hooks/useMap'
import { wgs84ToGCJ02 } from '@/utils/mapUtil'
import moment from 'moment'
import { getPolylineGroups, getMarkerGroups, getMoveLineArr, getAssetsFile } from './utils'
import type { ParkRecordData, VehicleTrackData } from '@/api/vehicle-track/vehicleTrack'

const sideOpened = ref(true) // 是否显示侧边筛选
const showSpeedController = ref(false) // 显示速度分段设置
const showSpeedChart = ref(false) // 显示速度曲线
const playerVal = ref(0)
let playerValCache = 0

const { map, AMap } = useMap('map-container', { pluginsOption: { ToolBar: plugin => new plugin({ position: 'RT', offset: [15, 14] }) } })

const echartOption = ref<Record<string, any>>({
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      type: 'cross'
    }
  },
  grid: {
    top: 40,
    bottom: 30,
    left: 70,
    right: 70
  },
  xAxis: {
    type: 'category',
    boundaryGap: false,
    data: []
  },
  yAxis: {
    name: '速度(km/h)',
    type: 'value',
    min: value => {
      Math.floor(value.min)
    },
    max: value => {
      Math.floor(value.Max)
    }
  },
  series: [
    {
      type: 'line',
      name: '速度',
      symbol: 'none',
      lineStyle: {
        color: '#98BDF5'
      },
      areaStyle: {
        color: '#C2D9F6'
      },
      data: [],
      markPoint: {
        itemStyle: { color: '#742CFF' },
        symbolSize: 30,
        data: []
      }
    }
  ]
})

let lineArr: VehicleTrackData[] = []
let newLine = []
const pointNumber = ref(100)
const isPlaying = ref(false)
const speedPerHour = ref(0)
const playerMultiple = ref(1)
let isInitVehicleLine = false
let isStopMove = false
let vehicleMarkerRef = null
let vehiclePolyLineGroup = null
let vehicleMarkerGroup = null

const onInitVehicleRoute = (data: VehicleTrackData[]) => {
  lineArr = data.map(v => {
    const centerPoint = wgs84ToGCJ02(v.lng, v.lat)
    const formatTime = moment(parseInt(v.gpsTime)).format('YYYY-MM-DD HH:mm:ss')
    return {
      ...v,
      lng: centerPoint[0],
      lat: centerPoint[1],
      date: formatTime
    }
  })

  initVehicleRoute()
}

// 初始化车辆轨迹
const initVehicleRoute = () => {
  map.value.clearMap()
  vehiclePolyLineGroup && map.value.remove(vehiclePolyLineGroup)
  initPlayer()
  initCarLine()
  initVehicleMarker()
  initChartData()
}

const initCarLine = () => {
  if (!lineArr.length) return
  vehiclePolyLineGroup = getPolylineGroups(AMap.value, lineArr, speedLeftPointX.value, speedrightPointX.value)
  map.value.add(vehiclePolyLineGroup)
  map.value.setFitView()
  isInitVehicleLine = true
}

// 初始化播放器
const initPlayer = () => {
  vehicleMarkerRef?.stopMove()
  isPlaying.value = false
  playerVal.value = 0
  playerValCache = 0
  pointNumber.value = lineArr.length ? lineArr.length - 1 : 100
  newLine = []
}

// 初始化起点、终点、车辆marker
const initVehicleMarker = async () => {
  if (!lineArr.length) return
  const { markerGroup, vehicleMarker } = await getMarkerGroups(AMap.value, lineArr)

  vehicleMarkerRef = vehicleMarker
  vehicleMarkerGroup = markerGroup
  map.value.add(vehicleMarkerGroup)

  // 绑定车辆移动事件
  vehicleMarkerRef.on('moving', function (e) {
    const position = e.target.getPosition()
    const passedPath = e.passedPath.length

    speedPerHour.value = newLine.length ? newLine[e.index].speed : lineArr[e.index].speed
    if (newLine.length) {
      playerVal.value = playerValCache + passedPath
    } else {
      playerVal.value = passedPath
    }
    if (playerVal.value === pointNumber.value) {
      initPlayer()
    }
    map.value.setCenter(position, false)
  })
}

// 初始化速度曲线
const initChartData = () => {
  echartOption.value.xAxis.data = lineArr.map(item => item.date)
  echartOption.value.series[0].data = lineArr.map(item => item.speed)
}

// 绘制停车点
const parkingMarkerGroup = []
let infoWindowInstance = null
const parkInfoRef = ref(null)
const parkInfo = ref<ParkRecordData>({})

const onRenderParkingMarker = (points: ParkRecordData[]) => {
  if (parkingMarkerGroup.length > 0) {
    map.value.remove(parkingMarkerGroup)
    echartOption.value.series[0].markPoint.data = []
  }
  const parkingIcon = new AMap.value.Icon({
    size: new AMap.value.Size(32, 32),
    image: getAssetsFile('map/park.png'),
    imageSize: new AMap.value.Size(32, 32),
    imageOffset: new AMap.value.Pixel(0, 0)
  })
  points.forEach(point => {
    const _point = wgs84ToGCJ02(point.lng, point.lat)
    const marker = new AMap.value.Marker({
      map: map.value,
      position: new AMap.value.LngLat(_point[0], _point[1]),
      icon: parkingIcon,
      offset: new AMap.value.Pixel(-13, -30)
    })
    marker.on('click', () => {
      onRenderParkingInfo(point)
    })
    parkingMarkerGroup.push(marker)

    echartOption.value.series[0].markPoint.data.push({
      value: 'p',
      xAxis: point.parkStartTime,
      yAxis: 0,
      label: { color: '#fff' }
    })
  })
}

// 绘制停车点信息窗体
const onRenderParkingInfo = async (data: ParkRecordData) => {
  createInfoWindowInstance()

  parkInfo.value = data
  const _point = wgs84ToGCJ02(data.lng, data.lat)
  infoWindowInstance.open(map.value, _point)
  map.value.setCenter(_point)
}

// 生成地图弹框实例
function createInfoWindowInstance() {
  if (infoWindowInstance) return
  infoWindowInstance = new AMap.value.InfoWindow({
    isCustom: true, //使用自定义窗体
    content: parkInfoRef.value.$el,
    offset: new AMap.value.Pixel(0, -25)
  })
}

// 关闭地图弹框
function handleCloseInfoWindow() {
  if (infoWindowInstance) infoWindowInstance.close()
}

// 播放车辆轨迹动画
const playVehicleRoute = () => {
  if (!lineArr.length) {
    ElMessage.warning('暂无行驶轨迹支持播放!')
    return
  }
  isPlaying.value = true
  if (isStopMove) {
    vehicleMarkerRef.resumeMove()
  } else if (newLine.length) {
    vehicleMarkerRef.moveAlong(getMoveLineArr(newLine), {
      duration: 100 / playerMultiple.value,
      autoRotation: true
    })
  } else {
    // 开启车辆移动
    vehicleMarkerRef.moveAlong(getMoveLineArr(lineArr), {
      duration: 100,
      autoRotation: true
    })
  }
}

// 暂停车辆轨迹动画
const pauseVehicleRoute = () => {
  isPlaying.value = false
  isStopMove = true
  vehicleMarkerRef.pauseMove()
}

const handleSpeedCommand = (command: number) => {
  playerMultiple.value = command
  playerValCache = playerVal.value
  newLine = lineArr.slice(playerValCache + 1)
  playVehicleRoute()
}

// 车辆轨迹进度条拖拽事件
const onSliderDrag = index => {
  if (!isInitVehicleLine) return
  const point = lineArr[index]
  vehicleMarkerRef.stopMove()
  vehicleMarkerRef.setPosition([point.lng, point.lat])
  vehicleMarkerRef.setAngle(point.course ?? 0)
  map.value.setCenter(point, false)
}

// 车辆轨迹进度条拖拽借结束事件
const onSliderChange = index => {
  // 由于高德地图无法指定从哪个点开始播放轨迹动画，所以要截取剩余路径播放
  newLine = lineArr.slice(index + 1)
  isPlaying.value = false
  isStopMove = false
  // 记录拖拽前位置
  playerValCache = playerVal.value
}

// 计算车速范围滑块位置
const speedRange = ref([12, 90])
const speedLeftPointX = ref(12)
const speedrightPointX = ref(90)

const speedRangeWidth = computed(() => {
  const leftWidth = ((speedLeftPointX.value / 120) * 100).toFixed(4)
  const rightWidth = (((120 - speedrightPointX.value) / 120) * 100).toFixed(4)
  return [leftWidth, rightWidth]
})

const onSpeedRangeChange = (e, render: boolean) => {
  speedLeftPointX.value = e[0]
  speedrightPointX.value = e[1]

  if (!lineArr.length || !render) return

  map.value.remove(vehiclePolyLineGroup)
  initCarLine()
}

onBeforeUnmount(() => {
  if (vehicleMarkerRef) {
    vehicleMarkerRef.stopMove()
  }
})

/** --------- 地图POI检索部分  -------- */
let searchMarker = null
const markerRef = ref(null)
const keyword = ref('')

// 地址检索
function handleSearch(queryString, cb) {
  const placeSearch = new AMap.value.PlaceSearch({
    city: '',
    pageSize: 10, // 单页显示结果条数
    pageIndex: 1, // 页码
    citylimit: false // 是否强制限制在设置的城市内搜索
  })
  placeSearch.search(queryString, (status, result) => {
    if (result && typeof result === 'object' && result.poiList) {
      const list = result.poiList.pois
      list.forEach(item => {
        item.value = item.name
        item.label = item.name
      })
      cb(list)
    } else {
      cb([])
    }
  })
}
// 地址选择
function handleSelect(item) {
  if (searchMarker) clearMarker()

  searchMarker = new AMap.value.Marker({
    position: item.location,
    content: markerRef.value.$el,
    offset: new AMap.value.Pixel(-13, -30)
  })

  map.value.add(searchMarker)
  map.value.setFitView(searchMarker)
}
// 清除标记点
const clearMarker = () => {
  map.value.remove(searchMarker)
  searchMarker = null
}
</script>

<style lang="scss" scoped>
.left {
  width: 370px;
  border-right: 1px solid var(--el-border-color);
  background-color: var(--el-bg-color);
}

.fold-inner-left {
  width: 0;
  transition: width 0.28s;
  overflow: hidden;
  border-right: none;
}

.hamburger-container {
  width: 26px;
  height: 70px;
  background: url(../../assets/images/hamburger-bg.png) no-repeat;
  background-size: 100% 100%;
  cursor: pointer;
  line-height: 70px;
  text-align: center;
  box-sizing: border-box;
  margin-left: -6px;
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  .fold-inner {
    transform: rotate(0);
    transition: transform 0.25s ease-in;
    &-rotate {
      transform: rotate(180deg);
      transition: transform 0.25s ease-out;
    }
  }
}

.trajectory-player {
  width: 100%;
  height: 50px;
  padding: 0px 70px;
  position: absolute;
  font-size: 14px;
  left: 0;
  bottom: 0;
  background-color: $menu-background;
  z-index: 260;
  border-top: 1px solid var(--el-border-color);

  :deep(.el-slider__button) {
    position: relative;
    width: 16px;
    height: 16px;
  }

  .showBaseLine {
    :deep(.el-slider__button) {
      &::after {
        content: '';
        width: 0;
        height: 120px;
        border-right: 1px dashed var(--el-color-primary);
        position: absolute;
        top: -120px;
      }
    }
  }

  .player-controler {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .player-controler-right,
  .player-controler-left {
    display: flex;
    align-items: center;
    color: $menu-text;
    cursor: pointer;

    span {
      cursor: pointer;
    }
  }

  .el-dropdown-link {
    color: $menu-text;
    line-height: 21px;
  }

  .speed-controler {
    width: 500px;
    height: 100px;
    position: absolute;
    right: 0;
    top: -100px;

    .title {
      height: 28px;
      color: #fff;
      background-color: rgba($color: #000000, $alpha: 0.8);
      padding: 0 12px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      cursor: pointer;
    }
    .content {
      height: 80px;
      padding: 0 20px 0 20px;
      background-color: rgba($color: #000000, $alpha: 0.4);
      display: flex;

      .content-left {
        font-size: 14px;
        color: #fff;
        margin-right: 10px;
        line-height: 32px;
      }

      .content-right {
        flex: 1;
      }

      :deep(.el-slider) {
        float: left;
      }

      :deep(.el-slider__bar) {
        background-color: #00ff00;
      }

      :deep(.el-slider__button-wrapper) {
        z-index: 10;
      }

      .progress-box {
        height: 36px;
        position: relative;
        padding-top: 6px;
      }

      .progress-left-blue,
      .progress-right-red {
        height: 6px;
        border-radius: 3px;
        z-index: 2;
      }

      .progress-left-blue {
        // float: left;
        background-color: #0066ff;
        position: absolute;
        // position: relative;
        top: 15px;
      }

      .progress-right-red {
        float: right;
        background-color: #ff3300;
        // position: relative;
        position: absolute;
        top: 15px;
        right: 0;
      }

      .scale-mark {
        width: 100%;
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
      }

      .mark-line {
        display: flex;
        flex-direction: column;
        align-items: center;
      }

      .line {
        display: inline-block;
        width: 1px;
        height: 5px;
        background-color: #fff;
        position: relative;
      }

      .long-line {
        height: 10px;
      }

      .mark-num {
        color: #fff;
        font-size: 12px;
        position: absolute;
        top: 10px;
        transform: translateX(-50%);
      }
    }
  }
}

.speed-chart-container {
  width: 100%;
  height: 140px;
  position: absolute;
  background-color: var(--el-bg-color);
  bottom: -90px;
  left: 0;
  z-index: 10;
  .speed-chart-btn {
    width: 120px;
    height: 28px;
    font-size: 14px;
    text-align: center;
    line-height: 28px;
    color: var(--el-text-color-primary);
    background-color: var(--el-bg-color);
    border: 1px solid var(--el-border-color);
    border-top-right-radius: 6px;
    border-top-left-radius: 6px;
    border-bottom: none;
    position: absolute;
    top: -28px;
    left: 50%;
    transform: translateX(-50%);
    cursor: pointer;
  }

  &.slide-up {
    animation: slideUp 0.3s ease-in forwards;
  }

  &.slide-down {
    animation: slideDown 0.3s ease-out forwards;
  }
}

@keyframes slideUp {
  from {
    transform: translateY(0);
  }
  to {
    transform: translateY(-140px);
  }
}

@keyframes slideDown {
  from {
    transform: translateY(-140px);
  }
  to {
    transform: translateY(0);
  }
}
</style>
